#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <malloc.h>
#include "bmp.h"

using std::endl;
using std::cin;
using std::cout;
using std::cerr;

#define WIDTHBYTES(bits) (((bits)+31)/32*4) //modify the size 
 
int main(int argc,char *argv[]) {
    BITMAPFILEHEADER bitHead, writebitHead;
    BITMAPINFOHEADER bitInfoHead, writebitInfoHead;

    cout<<sizeof(BITMAPFILEHEADER);

    // FILE* pfile;//input file  
    // FILE* wfile;//output file
    
    // ensure proper usage
    if(argc!=4) {
        cerr<<"Usage: transform infile outfile resolution"<<endl;
        return 1;
    }

    //remember filenames
    char *infile=argv[1];
    char *outfile=argv[2];
    char *opt=argv[3];

    //open infile and check the validation
    FILE* pfile=fopen(infile, "rb"); 
    if(pfile==NULL) {
        cerr<<"Could not open "<<infile<<endl;
        return 2;
    }

    //open outfile and check the validation
    FILE* wfile=fopen(outfile, "wb");
    if(wfile==NULL) {
        fclose(pfile);
        cerr<<"Could not create "<<outfile<<endl;
        return 3;
    }

    //check the file format
    WORD fileType;
    fread(&fileType, 1, sizeof(WORD), pfile);
    fwrite(&fileType, 1, sizeof(WORD), wfile);
    if (fileType != 0x4d42) {
        cerr<<"Unsupport file format"<<endl;
        return 5;
    }

    //check out the validation of opt and determine the parameters
    int mul_pos=-1;
    int output_height,output_width;
    for(int i=0;i<strlen(opt);i++) {
        if(!isdigit(opt[i])) {
            if(opt[i]!='*') {
                cerr<<"Usage: ./transform infile outfile resolution"<<endl;
                return 4;
            } else {
                mul_pos=i;
                break;
            }
        }
    }
    if(mul_pos==-1) {
        cerr<<"Usage: transform infile outfile resolution"<<endl;
        return 4;
    } else {
        output_height=output_width=0;
        for(int i=0;i<mul_pos;i++) {
            output_height=output_height*10+opt[i]-'0';
        }
        for(int i=mul_pos+1;i<strlen(opt);i++) {
            output_width=output_width*10+opt[i]-'0';
        }
    }

    //read header
    fread(&bitHead, 1, sizeof(BITMAPFILEHEADER), pfile);
    writebitHead = bitHead;

    //read infoheader
    fread(&bitInfoHead, 1, sizeof(BITMAPINFOHEADER), pfile);
    writebitInfoHead = bitInfoHead;  
 
    //specify the size of output image(write its header)
    writebitInfoHead.biHeight = output_height;
    writebitInfoHead.biWidth = output_width;
    int mywritewidth = WIDTHBYTES(writebitInfoHead.biWidth*writebitInfoHead.biBitCount);
    writebitInfoHead.biSizeImage = mywritewidth*writebitInfoHead.biHeight;
 
    writebitHead.bfSize = 54 + writebitInfoHead.biSizeImage;
    fwrite(&writebitHead, 1, sizeof(BITMAPFILEHEADER), wfile);
    fwrite(&writebitInfoHead, 1, sizeof(BITMAPINFOHEADER), wfile);
 
    int width = bitInfoHead.biWidth;
    int height = bitInfoHead.biHeight;
    //manage memory
    int l_width = WIDTHBYTES(width*bitInfoHead.biBitCount); 
    int write_width = WIDTHBYTES(writebitInfoHead.biWidth*writebitInfoHead.biBitCount);
 
    BYTE *pColorData = (BYTE *)malloc(height*l_width);
    memset(pColorData, 0, height*l_width);
 
    BYTE    *pColorDataMid = (BYTE *)malloc(mywritewidth*output_height);
    memset(pColorDataMid, 0, mywritewidth*output_height);
 
    long nData = height*l_width;
    long write_nData = mywritewidth*output_height;

    fread(pColorData, 1, nData, pfile);
 
    //image processing
    for (int hnum = 0; hnum < output_height; hnum++)
        for (int wnum = 0; wnum < output_width; wnum++) {
            double d_original_img_hnum = hnum*height / (double)output_height;
            double d_original_img_wnum = wnum*width / (double)output_width;
            int i_original_img_hnum = d_original_img_hnum;
            int i_original_img_wnum = d_original_img_wnum;
            double distance_to_a_x = d_original_img_wnum - i_original_img_wnum; 
            double distance_to_a_y = d_original_img_hnum - i_original_img_hnum; 
 
            int original_point_a = i_original_img_hnum*l_width + i_original_img_wnum * 3;   
            int original_point_b = i_original_img_hnum*l_width + (i_original_img_wnum + 1) * 3;  
            int original_point_c = (i_original_img_hnum + 1)*l_width + i_original_img_wnum * 3;   
            int original_point_d = (i_original_img_hnum + 1)*l_width + (i_original_img_wnum + 1) * 3;
            if (i_original_img_hnum +1 >= width) {
                original_point_c = original_point_a;
                original_point_d = original_point_b;
            }
            if (i_original_img_wnum +1 >= height) {
                original_point_b = original_point_a;
                original_point_d = original_point_c;
            }
 
            int pixel_point = hnum*write_width + wnum * 3; 
            for(int i = 0; i < 3; i++) {
                pColorDataMid[pixel_point + i] =
                pColorData[original_point_a + i] * (1 - distance_to_a_x)*(1 - distance_to_a_y) +
                pColorData[original_point_b + i] * distance_to_a_x*(1 - distance_to_a_y) +
                pColorData[original_point_c + i] * distance_to_a_y*(1 - distance_to_a_x) +
                pColorData[original_point_c + i] * distance_to_a_y*distance_to_a_x;
            }
        }
 
    fwrite(pColorDataMid, 1, write_nData, wfile);   //write output image  
    fclose(pfile);
    fclose(wfile);

    cout<<"succesefully create outfile."<<endl;
    return 0;
}